import sys
from copy import copy
from functools import cached_property
from typing import Literal

from paramiko.client import SSHClient
from tencentcloud.common.credential import Credential
from tencentcloud.cvm.v20170312.cvm_client import CvmClient
from zkl_serialization import JsonValue

from jobs_agency.basics import JobsStore
from jobs_agency.platforms.cloud import CloudAttachedJob, CloudPlatform
from jobs_agency.platforms.ssh import SSHConfig, SSHPlan
from .config import TencentCloudClientConfig, TencentCloudCvmConfig
from .utils_cvm import create_instance, get_instance_ip_or_retry, get_instance_state, terminate_instance
from .utils_life import make_prepare_func, make_release_script


class TencentCloudCvmPlatform(CloudPlatform[str, TencentCloudCvmConfig]):
    def __init__(self,
        store: JobsStore,
        tcc_config: TencentCloudClientConfig,
        network: Literal['public', 'private'],
    ):
        super().__init__(store)
        self._tcc_config = tcc_config
        self._network = network

    @cached_property
    def cvm_client(self) -> CvmClient:
        credential = Credential(self._tcc_config.secret_id, self._tcc_config.secret_key)
        return CvmClient(credential, self._tcc_config.region)

    @property
    def identifier(self) -> JsonValue:
        return {
            **super().identifier,
            'secret_id': self._tcc_config.secret_id,
            'region':    self._tcc_config.region
        }

    def _allocate_instance(self, config: TencentCloudCvmConfig) -> str:
        return create_instance(self.cvm_client, config.cvm_request)

    def _configure_ssh(self, config: TencentCloudCvmConfig, instance_id: str) -> SSHConfig:
        instance_ip = get_instance_ip_or_retry(self.cvm_client, instance_id, network=self._network)
        ssh_config = copy(config.ssh)
        ssh_config.hostname = instance_ip
        return ssh_config

    def _configure_plan(self, attached_job: CloudAttachedJob, plan: SSHPlan) -> SSHPlan:
        prepare_func_platform = make_prepare_func(self._acc_config)
        prepare_func_plan = plan.prepare_func

        def prepare_func(ssh_client: SSHClient):
            print("Prepare func (for detached job) from platform...", file=sys.stderr)
            prepare_func_platform(ssh_client)

            if prepare_func_plan:
                print("Prepare func (for detached job) from plan...", file=sys.stderr)
                prepare_func_plan(ssh_client)

            print("Preparation (for detached job) done!", file=sys.stderr)

        release_script_platform = make_release_script(attached_job.instance_id)
        release_script_config = attached_job.release_script
        release_script_plan = plan.release_script

        release_script = ""
        if release_script_plan:
            release_script += "echo 'Release script from plan...' >&2\n"
            release_script += release_script_plan + "\n"
        if release_script_config:
            release_script += "echo 'Release script from config...' >&2\n"
            release_script += release_script_config + "\n"
        release_script += "echo 'Release script from platform...' >&2\n"
        release_script += release_script_platform + "\n"
        release_script += "echo 'Releasing done!' >&2\n"

        plan = copy(plan)
        plan.prepare_func = prepare_func
        plan.release_script = release_script
        return plan

    def _check_instance_running(self, instance_id: str) -> bool:
        state = get_instance_state(self.cvm_client, instance_id)
        return state not in ('SHUTDOWN', 'TERMINATING', None)

    def _release_instance(self, instance_id: str):
        terminate_instance(self.cvm_client, instance_id)
